﻿using FySystem.Secure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IOT.Protocols
{
    public class ModbusUtils
    {
        /// <summary>
        /// 03功能码多寄存器读取组包
        /// </summary>
        /// <param name="slaveAddr">从机地址</param>
        /// <param name="addrH">寄存器地址高位</param>
        /// <param name="addrL">寄存器地址低位</param>
        /// <param name="lH">寄存器个数高位</param>
        /// <param name="lL">寄存器个数低位</param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public static byte[] GenRead03(byte slaveAddr, byte addrH, byte addrL, byte lH, byte lL)
        {
            byte[] res=new byte[8];
            try
            {
                res[0] = slaveAddr;
                res[1] = 0x03;
                res[2] = addrH;
                res[3] = addrL;
                res[4] = lH;
                res[5] = lL;

                //crc
                byte[] crc = CRC.ModbusCrc16(res, 0, 6);
                res[6] = crc[0];
                res[7] = crc[1];
            }
            catch (Exception)
            {
                throw;
            }
            return res;
        }

        /// <summary>
        /// 04功能码批量读取组包
        /// </summary>
        /// <param name="slaveAddr">从机地址</param>
        /// <param name="addrH">起始寄存器地址高位</param>
        /// <param name="addrL">起始寄存器地址低位</param>
        /// <param name="cH">读取寄存器个数高位</param>
        /// <param name="cL">读取寄存器个数低位</param>
        /// <returns></returns>
        public static byte[] GenRead04(byte slaveAddr, byte addrH, byte addrL, byte cH, byte cL)
        {
            byte[] res = new byte[8];
            try
            {
                res[0] = slaveAddr;
                res[1] = 0x04;
                res[2] = addrH;
                res[3] = addrL;
                res[4] = cH;
                res[5] = cL;

                //crc
                byte[] crc = CRC.ModbusCrc16(res, 0, 6);
                res[6] = crc[0];
                res[7] = crc[1];
            }
            catch (Exception)
            {
                throw;
            }
            return res;
        }

        /// <summary>
        /// 06功能码单寄存器写入组包
        /// </summary>
        /// <param name="slaveAddr"></param>
        /// <param name="addrH"></param>
        /// <param name="addrL"></param>
        /// <param name="valH"></param>
        /// <param name="valL"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public static byte[] GenWrite06(byte slaveAddr, byte addrH, byte addrL, byte valH, byte valL)
        {
            byte[] res = new byte[8];
            try
            {
                res[0] = slaveAddr;
                res[1] = 0x06;
                res[2] = addrH;
                res[3] = addrL;
                res[4] = valH;
                res[5] = valL;

                //crc
                byte[] crc = CRC.ModbusCrc16(res, 0, 6);
                res[6] = crc[0];
                res[7] = crc[1];
            }
            catch (Exception)
            {
                throw;
            }
            return res;
        }

        /// <summary>
        /// 对数据进行简单的校验
        /// </summary>
        /// <param name="span"></param>
        /// <exception cref="NotImplementedException"></exception>
        public static void CheckData(Span<byte> span)
        {
            try
            {
                if (span.Length <= 2)
                    throw new Exception("数据长度小于3");

                byte[] crc = CRC.ModbusCrc16(span.Slice(0, span.Length - 2));
                if (crc[0] != span[span.Length - 2] || crc[1] != span[span.Length - 1])
                    throw new Exception("CRC校验失败");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 对数据进行简单的校验，失败时返回false
        /// </summary>
        /// <param name="span"></param>
        /// <returns></returns>
        public static bool CheckDataForRet(Span<byte> span)
        {
            try
            {
                if (span.Length <= 2)
                    return false;

                byte[] crc = CRC.ModbusCrc16(span.Slice(0, span.Length - 2));
                if (crc[0] != span[span.Length - 2] || crc[1] != span[span.Length - 1])
                    return false;
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }
    }
}
